package generator2;

import java.awt.Rectangle;
import java.util.*;

import drawables.*;

/**
 * Abstract class for reporting the computed moving objects. Non-abstract
 * subclasses are generator2.DefaultReporter and generator2.OracleReporter.
 * 
 * @version 2.10 27.08.2003 additional parameters reported, use of
 *          RandomGenerator
 * @version 2.00 06.07.2001 complete revision
 * @version 1.50 15.06.2001 report of external objects added
 * @version 1.40 28.10.2000 report number and probability added
 * @version 1.30 10.10.2000 report of new/disappearing object and edges added
 * @version 1.20 21.04.2000 report of real numbers
 * @version 1.10 01.02.2000 new order of reporting
 * @version 1.00 18.01.2000 first version
 * @author FH Oldenburg
 */
public class Reporter {

	/**
	 * Action code "new object".
	 */
	public static final byte NEW_OBJECT = 0;
	/**
	 * Action code "moving object".
	 */
	public static final byte MOVE_OBJECT = 1;
	/**
	 * Action code "disappering object".
	 */
	public static final byte DEL_OBJECT = 2;

	/**
	 * Properties of the generator.
	 */
	protected Properties properties = null;
	/**
	 * Should the objects be visualized?
	 */
	protected boolean visualize = false;
	/**
	 * Name of property indicating a visualization.
	 */
	public static final String VIZ = "VIZ";

	/**
	 * Layer of the drawable objects.
	 */
	protected static final int SYMBOLLAYER = 4;
	/**
	 * Container of drawable objects.
	 */
	protected DrawableObjects objects = null;

	/**
	 * Number of reported points.
	 */
	protected int numOfPoints = 0;
	/**
	 * Number of reported edges.
	 */
	protected int numOfEdges = 0;

	/**
	 * The random generator.
	 */
	protected Random random;

	/**
	 * Reporter constructor. Must be called by constructors of subclasses.
	 * 
	 * @param properties
	 *            properties of the generator
	 * @param objects
	 *            container of drawable objects
	 */
	public Reporter(Properties properties, DrawableObjects objects) {
		this.properties = properties;
		this.objects = objects;
		this.visualize = properties.getProperty(VIZ) != null;
		this.random = RandomGenerator.getForReport(properties);
	}

	/**
	 * Does nothing. Can be overwritten for closing the reporter.
	 */
	public void close() {
	}

	/**
	 * Returns the number of reported edges.
	 * 
	 * @return number of reported edges
	 */
	public int getNumberOfReportedEdges() {
		return numOfEdges;
	}

	/**
	 * Returns the number of reported points.
	 * 
	 * @return number of reported points
	 */
	public int getNumberOfReportedPoints() {
		return numOfPoints;
	}

	/**
	 * Removes the reported moving objects.
	 */
	public void removeReportedObjects() {
		if (visualize && (objects != null))
			objects.removeAllObjectsOfLayer(SYMBOLLAYER);
	}

	/**
	 * Does nothing. Can be overwritten for reporting a disappearing external
	 * object.
	 * 
	 * @param time
	 *            time stamp
	 * @param id
	 *            object id
	 * @param repNum
	 *            report number
	 * @param objClass
	 *            object class
	 * @param rect
	 *            the area
	 */
	public void reportDisappearingExternalObject(int time, long id, int repNum,
			int objClass, Rectangle rect) {
	}

	/**
	 * Does nothing. Should be overwritten for reporting the characteristic
	 * properties of a dispappering object at a time stamp
	 * 
	 * @param time
	 *            the arrival time (with fraction)
	 * @param id
	 *            object id
	 * @param repNum
	 *            report number
	 * @param objClass
	 *            object class
	 * @param x
	 *            x-coordinate
	 * @param y
	 *            y-coordinate
	 * @param doneDist
	 *            the distance since the last reporting
	 * @param reportProbability
	 *            value between (0..1000)
	 */
	public void reportDisappearingObject(double time, long id, int repNum,
			int objClass, int x, int y, double doneDist, int reportProbability) {
	}

	/**
	 * Reports a double with an explaining text on the standard output.
	 * 
	 * @param text
	 *            explaining text
	 * @param value
	 *            output value
	 */
	public void reportDouble(String text, double value) {
		System.out.println(text + value);
	}

	/**
	 * Does nothing. Can be overwritten for reporting the coordinates of a
	 * traversed edge if the report probability > 0.
	 * 
	 * @param time
	 *            time stamp (with fraction) when the edge is entried
	 * @param objId
	 *            the id of the moving object id
	 * @param edgeRepNum
	 *            the edge report number
	 * @param objClass
	 *            object class
	 * @param edgeId
	 *            the edge id
	 * @param edgeClass
	 *            the edge class
	 * @param x1
	 *            the first (= current) x-coordinate
	 * @param y1
	 *            the first (= current) y-coordinate
	 * @param speed
	 *            current speed
	 * @param x2
	 *            the second (= later) x-coordinate
	 * @param y2
	 *            the second (= later) y-coordinate
	 * @param reportProbability
	 *            (0..1000)
	 */
	public void reportEdge(double time, long objId, int edgeRepNum,
			int objClass, long edgeId, int edgeClass, int x1, int y1,
			double speed, int x2, int y2, int reportProbability) {
	}

	/**
	 * Reports an integer number with an explaining text on the stardard output.
	 * 
	 * @param text
	 *            explaining text
	 * @param value
	 *            output value
	 */
	public void reportInt(String text, long value) {
		System.out.println(text + value);
	}

	/**
	 * Reports a moving external object.
	 * 
	 * @param time
	 *            time stamp
	 * @param id
	 *            object id
	 * @param repNum
	 *            report number
	 * @param objClass
	 *            object class
	 * @param rect
	 *            the area
	 */
	public void reportMovingExternalObject(int time, long id, int repNum,
			int objClass, Rectangle rect) {
		if (visualize && (objects != null))
			visualizeExternalObject(rect, objClass, time);
	}

	/**
	 * Reports the characteristic properties of a moving object at a time stamp
	 * according to its report probability.
	 * 
	 * @return new report number
	 * @param time
	 *            time stamp
	 * @param id
	 *            object id
	 * @param repNum
	 *            report number
	 * @param objClass
	 *            object class
	 * @param x
	 *            x-coordinate
	 * @param y
	 *            y-coordinate
	 * @param speed
	 *            current speed
	 * @param doneDist
	 *            the distance since the last reporting
	 * @param nextNodeX
	 *            x-coordinate of the next node
	 * @param nextNodeY
	 *            y-coordinate of the next node
	 * @param reportProbability
	 *            value between (0..1000)
	 */
	public int reportMovingObject(int time, long id, int repNum, int objClass,
			double x, double y, double speed, double doneDist, int nextNodeX,
			int nextNodeY, int reportProbability) {
		if (reportProbability == 0)
			return repNum;
		if (Math.abs(random.nextInt()) % 1000 >= reportProbability)
			return repNum;
		numOfPoints++;
		repNum++;
		if (visualize && (objects != null))
			visualizeMovingObject((int) x, (int) y, objClass, time);
		return repNum;
	}

	/**
	 * Reports a new external object.
	 * 
	 * @param time
	 *            time stamp
	 * @param id
	 *            object id
	 * @param objClass
	 *            object class
	 * @param rect
	 *            the area
	 */
	public void reportNewExternalObject(int time, long id, int objClass,
			Rectangle rect) {
		if (visualize && (objects != null))
			visualizeExternalObject(rect, objClass, time);
	}

	/**
	 * Reports an new moving object if its report probability > 0.
	 * 
	 * @param time
	 *            time stamp
	 * @param id
	 *            object id
	 * @param objClass
	 *            object class
	 * @param x
	 *            x-coordinate of start
	 * @param y
	 *            y-coordinate of start
	 * @param speed
	 *            current speed
	 * @param nextNodeX
	 *            x-coordinate of the next node
	 * @param nextNodeY
	 *            y-coordinate of the next node
	 * @param reportProbability
	 *            value between (0..1000)
	 */
	public int reportNewMovingObject(int time, long id, int objClass, int x,
			int y, double speed, int nextNodeX, int nextNodeY,
			int reportProbability) {
		int repNum = 0;
		if (reportProbability > 0) {
			numOfPoints++;
			repNum++;
			if (visualize && (objects != null))
				visualizeMovingObject(x, y, objClass, time);
		}
		return repNum;
	}
	
	/**
	 * Xiaohui: newer version: also report the edge id 
	 * Reports an new moving object if its report probability > 0.
	 * 
	 * @param time
	 *            time stamp
	 * @param id
	 *            object id
	 * @param objClass
	 *            object class
	 * @param x
	 *            x-coordinate of start
	 * @param y
	 *            y-coordinate of start
	 * @param speed
	 *            current speed
	 * @param nextNodeX
	 *            x-coordinate of the next node
	 * @param nextNodeY
	 *            y-coordinate of the next node
	 * @param reportProbability
	 *            value between (0..1000)
	 */
	public int reportNewMovingObject(int time, long id, int objClass, int x,
			int y, double speed, long roadid, int nextNodeX, int nextNodeY,
			int reportProbability) {
		int repNum = 0;
		if (reportProbability > 0) {
			numOfPoints++;
			repNum++;
			if (visualize && (objects != null))
				visualizeMovingObject(x, y, objClass, time);
		}
		return repNum;
	}
	
	

	/**
	 * Visualizes an external object.
	 * 
	 * @param rect
	 *            the object as rectangle
	 * @param objClass
	 *            object class
	 * @param time
	 *            time stamp
	 */
	protected void visualizeExternalObject(Rectangle rect, int objClass,
			int time) {
		DrawableRectangle area = new DrawableRectangle(rect.x, rect.y, rect.x
				+ rect.width, rect.y + rect.height, false, "Rectangle"
				+ objClass + "-" + time);
		area.setLayer(SYMBOLLAYER);
		objects.addDrawable(area);
	}

	/**
	 * Visualizes a moving object.
	 * 
	 * @param x
	 *            x
	 * @param y
	 *            x
	 * @param objClass
	 *            object class
	 * @param time
	 *            time stamp
	 */
	protected void visualizeMovingObject(int x, int y, int objClass, int time) {
		DrawableSymbol symbol = new DrawableSymbol(x, y, "Point" + objClass
				+ "-" + time);
		symbol.setLayer(SYMBOLLAYER);
		objects.addDrawable(symbol);
	}

}